// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package types

import (
	"go/ast"
	"go/token"
)

// ---------------------------------------------------------------
// API 

// 联合表示嵌入在接口中的术语的联合。
type Union struct {
	terms []*Term // 语法术语列表（非规范化术语列表）
}

// NewUnion返回具有给定术语的新联合类型。
// 创建空联合是错误的；它们在句法上是不可能的。
func NewUnion(terms []*Term) *Union {
	if len(terms) == 0 {
		panic("empty union")
	}
	return &Union{terms}
}

func (u *Union) Len() int         { return len(u.terms) }
func (u *Union) Term(i int) *Term { return u.terms[i] }

func (u *Union) Underlying() Type { return u }
func (u *Union) String() string   { return TypeString(u, nil) }

// 一个任期代表一个联盟的任期。
type Term term

// NewTerm返回一个新的联合术语。
func NewTerm(tilde bool, typ Type) *Term { return &Term{tilde, typ} }

func (t *Term) Tilde() bool    { return t.tilde }
func (t *Term) Type() Type     { return t.typ }
func (t *Term) String() string { return (*term)(t).String() }

// ---------------------------------------------------------------
// 实现

// 避免由于二次项列表操作而导致过多的类型检查时间。
const maxTermCount = 100

// parseUnion将uexpr解析为表达式的并集。
// 结果是一个联合类型，或者对于某些错误，结果是Typ[Invalid]。
func parseUnion(check *Checker, uexpr ast.Expr) Type {
	blist, tlist := flattenUnion(nil, uexpr)
	assert(len(blist) == len(tlist)-1)

	var terms []*Term

	var u Type
	for i, x := range tlist {
		term := parseTilde(check, x)
		if len(tlist) == 1 && !term.tilde {
			// 单一类型。可以提前返回，因为所有相关的
			// 检查都已在parseTilde中执行（无需在下面进行期限有效性检查）。
			return term.typ // 类型已通过检查记录。parseTilde 
		}
		if len(terms) >= maxTermCount {
			if u != Typ[Invalid] {
				check.errorf(x, _InvalidUnion, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
				u = Typ[Invalid]
			}
		} else {
			terms = append(terms, term)
			u = &Union{terms}
		}

		if i > 0 {
			check.recordTypeAndValue(blist[i-1], typexpr, u, nil)
		}
	}

	if u == Typ[Invalid] {
		return u
	}

	// /中的类型检查条款的有效性。shu jian defg
	// 注意：这是一个二次算法，但联合往往很短。
	check.later(func() {
		for i, t := range terms {
			if t.typ == Typ[Invalid] {
				continue
			}

			u := under(t.typ)
			f, _ := u.(*Interface)
			if t.tilde {
				if f != nil {
					check.errorf(tlist[i], _InvalidUnion, "invalid use of ~ (%s is an interface)", t.typ)
					continue // 不要为t报告另一个错误
				}

				if !Identical(u, t.typ) {
					check.errorf(tlist[i], _InvalidUnion, "invalid use of ~ (underlying type of %s is %s)", t.typ, u)
					continue
				}
			}

			// 独立嵌入式接口正常，并在开始时由单一类型案例
			// 处理。上面不包括带有tilde的嵌入式接口。如果我们在这里找到
			// 的话，我们必须在语法术语列表中至少有两个术语（但不一定在联合类型集的术语列表中有
			// ）。
			if f != nil {
				tset := f.typeSet()
				switch {
				case tset.NumMethods() != 0:
					check.errorf(tlist[i], _InvalidUnion, "cannot use %s in union (%s contains methods)", t, t)
				case t.typ == universeComparable.Type():
					check.error(tlist[i], _InvalidUnion, "cannot use comparable in union")
				case tset.comparable:
					check.errorf(tlist[i], _InvalidUnion, "cannot use %s in union (%s embeds comparable)", t, t)
				}
				continue // 具有接口类型的术语不受无重叠规则
			}

			// 报告重叠（非不相交）的术语，例如
			// a | a、a | ~a、~a | ~a和~a | a（其中在（a）==a下）。
			if j := overlappingTerm(terms[:i], t); j >= 0 {
				check.softErrorf(tlist[i], _InvalidUnion, "overlapping terms %s and %s", t, terms[j])
			}
		}
	})

	return u
}

func parseTilde(check *Checker, tx ast.Expr) *Term {
	x := tx
	var tilde bool
	if op, _ := x.(*ast.UnaryExpr); op != nil && op.Op == token.TILDE {
		x = op.X
		tilde = true
	}
	typ := check.typ(x)
	// 不允许嵌入独立类型参数（问题#47127）。
	// 如果我们将类型的基础类型
	// 参数设置为约束接口，则不再需要此限制：如果我们嵌入一个单独的类型参数，我们将
	// 只需使用其基础类型（就像我们对其他命名的嵌入式接口所做的那样），
	// 由于底层类型是接口，因此嵌入定义良好。
	if isTypeParam(typ) {
		check.error(x, _MisplacedTypeParam, "cannot embed a type parameter")
		typ = Typ[Invalid]
	}
	term := NewTerm(tilde, typ)
	if tilde {
		check.recordTypeAndValue(tx, typexpr, &Union{[]*Term{term}}, nil)
	}
	return term
}

// overlappingTerm报告术语x的索引，其术语为
// 与y重叠（非不相交）。如果没有
// 该术语，则结果为<0。术语y的类型不能是接口，在术语列表中忽略具有接口类型的术语
// 。
func overlappingTerm(terms []*Term, y *Term) int {
	assert(!IsInterface(y.typ))
	for i, x := range terms {
		if IsInterface(x.typ) {
			continue
		}
		// 不相交需要非nil、非top参数、
		// 和非接口类型作为术语类型。
		if debug {
			if x == nil || x.typ == nil || y == nil || y.typ == nil {
				panic("empty or top union term")
			}
		}
		if !(*term)(x).disjoint((*term)(y)) {
			return i
		}
	}
	return -1
}

// flattle union走的是形式为a | B | C |的union类型表达式。。。，
// 同时提取二进制表达式（blist）和叶类型（tlist）。
func flattenUnion(list []ast.Expr, x ast.Expr) (blist, tlist []ast.Expr) {
	if o, _ := x.(*ast.BinaryExpr); o != nil && o.Op == token.OR {
		blist, tlist = flattenUnion(list, o.X)
		blist = append(blist, o)
		x = o.Y
	}
	return blist, append(tlist, x)
}
